package com.zxy.supplier_system.server.repository

import com.zxy.supplier_system.server.controller.ProcurementSupplierPaymentRecordController
import com.zxy.supplier_system.server.entity.ProcurementReturn
import com.zxy.supplier_system.server.entity.ProcurementReturnItem
import com.zxy.supplier_system.server.entity.ProcurementSupplier
import com.zxy.supplier_system.server.entity.SystemService
import com.zxy.supplier_system.server.utils.SpecificationUtils
import com.zxy.supplier_system.server.utils.joinOnce
import jakarta.persistence.EntityManager
import jakarta.persistence.PersistenceContext
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.domain.Specification
import org.springframework.data.jpa.repository.JpaRepository
import java.math.BigDecimal
import java.time.OffsetDateTime
import java.time.YearMonth
import java.time.ZoneId

@Suppress("FunctionName")
interface ProcurementReturnItemRepository:JpaRepository<ProcurementReturnItem, Int>,
    CustomProcurementReturnItemRepository {

    fun findAllByProcurementReturn_IdOrderBySequenceAsc(procurementReturn_id: Int, pageable: Pageable):Page<ProcurementReturnItem>

    fun existsByProcurementReturn_IdAndGoods_Id(procurementReturn_id: Int, goods_id: Int):Boolean

    fun existsByProcurementReturn_IdAndGoods_Barcode(procurementReturn_id: Int, goods_barcode: String):Boolean

    fun findByProcurementReturn_IdAndGoods_IdAndProcurementReturn_Supplier_SystemService(procurementReturn_id: Int, goods_id: Int,systemService: SystemService): ProcurementReturnItem?

    fun findFirstByProcurementReturn_Supplier_IdAndGoods_IdOrderByProcurementReturn_CreatedDateTimeDesc(market_id: Int, goods_id: Int):ProcurementReturnItem?

    fun findFirstByProcurementReturn_Supplier_IdAndGoods_IdAndSubGoods_IdOrderByProcurementReturn_CreatedDateTimeDesc(market_id: Int, goods_id: Int,subGoods_id: Int):ProcurementReturnItem?

    fun findByProcurementReturn_IdAndGoods_IdAndSubGoods_IdAndProcurementReturn_Supplier_SystemService(marketOrder_id: Int, goods_id: Int, subGoods_id: Int?,systemService: SystemService): ProcurementReturnItem?



    object Specifications{

        fun procurementReturnSubmitted(): Specification<ProcurementReturnItem> {
            return Specification { root, query, cb ->
                cb.isNotNull(
                    root.joinOnce<ProcurementReturnItem, ProcurementReturn>("procurementReturn").get<OffsetDateTime>("submittedDateTime")
                )
            }
        }

        fun systemServiceEqual(systemService: SystemService): Specification<ProcurementReturnItem> {
            return SystemServiceRepository.Specifications.systemServiceEquals({ root ->
                root.joinOnce<ProcurementReturnItem, ProcurementReturn>("procurementReturn").joinOnce("supplier")
            }, systemService)
        }

        fun supplierIdEqual(supplierId: Int?): Specification<ProcurementReturnItem> {
            return if (supplierId == null) SpecificationUtils.empty()
            else Specification { root, query, cb ->
                cb.equal(
                    root.joinOnce<ProcurementReturnItem, ProcurementReturn>("procurementReturn")
                        .joinOnce<ProcurementReturn, ProcurementSupplier>("supplier").get<Int>("id"), supplierId
                )
            }
        }

        fun createdDateTimeBefore(dateTime: OffsetDateTime?): Specification<ProcurementReturnItem> {
            return if (dateTime == null) SpecificationUtils.empty()
            else Specification { root, query, cb ->
                cb.lessThanOrEqualTo(
                    root.joinOnce<ProcurementReturnItem, ProcurementReturn>("procurementReturn")
                        .get<OffsetDateTime>("createdDateTime"),
                    dateTime
                )
            }
        }

        fun createdDateTimeLessThanOrEqual(yearMonth: YearMonth?): Specification<ProcurementReturnItem> {
            return if (yearMonth == null) SpecificationUtils.empty()
            else Specification { root, query, cb ->
                cb.lessThan(
                    root.joinOnce<ProcurementReturnItem, ProcurementReturn>("procurementReturn")
                        .get<OffsetDateTime>("createdDateTime"),
                    yearMonth.atEndOfMonth().plusDays(1).atStartOfDay()
                        .atOffset(OffsetDateTime.now(ZoneId.systemDefault()).offset)
                )
            }
        }

        fun createdDateTimeGreatThanOrEqual(yearMonth: YearMonth?): Specification<ProcurementReturnItem> {
            return if (yearMonth == null) SpecificationUtils.empty()
            else Specification { root, query, cb ->
                cb.greaterThanOrEqualTo(
                    root.joinOnce<ProcurementReturnItem, ProcurementReturn>("procurementReturn")
                        .get<OffsetDateTime>("createdDateTime"),
                    yearMonth.atDay(1).atStartOfDay()
                        .atOffset(OffsetDateTime.now(ZoneId.systemDefault()).offset)
                )
            }
        }

        fun systemServiceEquals(systemService: SystemService):Specification<ProcurementReturnItem> {
            return SystemServiceRepository.Specifications.systemServiceEquals({ r->r.joinOnce<ProcurementReturnItem, ProcurementReturn>("procurementReturn").joinOnce<ProcurementReturn, ProcurementSupplier>("supplier")},systemService)
        }


    }

}

interface CustomProcurementReturnItemRepository{
    fun sumBySupplier(
        supplierId: Int,
        request: ProcurementSupplierPaymentRecordController.QueryProcurementPaymentRecordRequest,
        systemService: SystemService
    ): BigDecimal
}

class CustomProcurementReturnItemRepositoryImpl:CustomProcurementReturnItemRepository{

    @PersistenceContext
    lateinit var entityManager: EntityManager
    override fun sumBySupplier(
        supplierId: Int,
        request: ProcurementSupplierPaymentRecordController.QueryProcurementPaymentRecordRequest,
        systemService: SystemService
    ): BigDecimal {
        val criteriaBuilder = entityManager.criteriaBuilder
        val query = criteriaBuilder.createQuery()
        val root = query.from(ProcurementReturnItem::class.java)
        query.select(
            criteriaBuilder.sum<BigDecimal>(
                criteriaBuilder.prod(
                    root.get<BigDecimal>("price"), root.get<Int>("amount").`as`(
                        BigDecimal::class.java
                    )
                )
            )
        )


        query.where(
            Specification.allOf(
                ProcurementReturnItemRepository.Specifications.systemServiceEqual(systemService),
                ProcurementReturnItemRepository.Specifications.supplierIdEqual(supplierId),
                ProcurementReturnItemRepository.Specifications.createdDateTimeBefore(request.endPaidDateTime),
                ProcurementReturnItemRepository.Specifications.procurementReturnSubmitted(),
            ).toPredicate(root, query, criteriaBuilder)
        )

        return (entityManager.createQuery(query).singleResult as BigDecimal?) ?: BigDecimal.ZERO
    }

}